/*
 * RepRapTableWriter.cpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   RepRapTableWriter.cpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   13 sep 2012
 */


#include <boost/regex.hpp>
#include <boost/format.hpp>
//#include <cmath>

#include "Temperature.hpp"
#include "AdcValue.hpp"
#include "ThermistorTable.hpp"
#include "ThermistorTableRow.hpp"
#include "TemperatureScale.hpp"
#include "LinearInterpolationThermistor.hpp"

#include "RepRapTableWriter.hpp"

namespace tltgen {

  RepRapTableWriter::RepRapTableWriter(const std::string& tableName, const shared_ptr<TemperatureScale>& outputScale) :
    tableName(tableName), outputScale(outputScale)
  {
    if (! isValidTableName(tableName)) {
      throw std::invalid_argument("Invalid table name specified.");
    }
    if (! isValidOutputScale(outputScale)) {
      throw std::invalid_argument("Invalid output scale specified.");
    }
  }

  bool RepRapTableWriter::isValidOutputScale(const shared_ptr<TemperatureScale>& outputScale)
  {
    return outputScale.get() != NULL;
  }
  const shared_ptr<TemperatureScale> RepRapTableWriter::getOutputScale() const
  {
    return this->outputScale;
  }

  bool RepRapTableWriter::isValidTableName(const std::string& tableName)
  {
    const boost::regex c_identifier("[A-Z,a-z,_][0-9,A-Z,a-z,_]*");
    return boost::regex_match(tableName, c_identifier);
  }
  const std::string RepRapTableWriter::getTableName() const
  {
    return this->tableName;
  }

  bool RepRapTableWriter::isValidThermistorTable(const shared_ptr<ThermistorTable>& thermistorTable) const
  {
    return thermistorTable.get() != NULL && thermistorTable->hasCircuit() && thermistorTable->hasAnalogToDigitalConverter() &&
      thermistorTable->getNbRows() >= 2;
  }  

  bool RepRapTableWriter::compareTemperatureDescending(const ThermistorTableRow* r1, const ThermistorTableRow* r2)
  {
    return r1->getTemperature() > r2->getTemperature();
  }

  void RepRapTableWriter::write(std::ostream& out, const shared_ptr<ThermistorTable>& thermistorTable)
  {
    if (! isValidThermistorTable(thermistorTable)) {
      throw std::invalid_argument("The given thermistor table is invalid.");
    }

    std::vector<ThermistorTableRow*> rows;
    for (std::list<ThermistorTableRow*>::const_iterator it = thermistorTable->begin(); it != thermistorTable->end(); ++it) {
      if ((*it)->hasValidTemperature() && (*it)->hasValidAdcValue()) {
	rows.push_back(*it);
      }
    }
    std::sort(rows.begin(), rows.end(), compareTemperatureDescending);

    LinearInterpolationThermistor thermistor(thermistorTable);

    out << "// Thermistor table generated by tltgen\n";
    //out << "//  " << thermistorTable->getThermistor() << "\n";
    //out << "//  " << electronics->getCircuit() << "\n";
    out << "const short " << getTableName() << "[][2] PROGMEM = {\n";
    for (std::vector<ThermistorTableRow *>::const_iterator it = rows.begin(); it != rows.end(); ++it) {
      Temperature kelvin_t = (*it)->getTemperature();
      Temperature t = getOutputScale()->fromKelvin(kelvin_t);
      Temperature rounded_t;
      if (it == rows.begin()) {
	rounded_t = floor(t);
      } else if (it == rows.end() - 1) {
	rounded_t = ceil(t);
      } else {
	rounded_t = floor(t + 0.5);
      }
      Temperature rounded_kelvin_t = getOutputScale()->toKelvin(rounded_t);
      Resistance r = thermistor.getResistance(rounded_kelvin_t);
      Voltage v = thermistorTable->getCircuit()->getOutputVoltage(r);
      AdcValue adc = thermistorTable->getAnalogToDigitalConverter()->getOutputValue(v);
      out << "    {" << formatAdc(adc) << "*OVERSAMPLENR" << ", " << formatTemperature(t) << "},\n";
    }
    out << "};\n";
  }



  std::string RepRapTableWriter::formatAdc(AdcValue adc) const
  {
    boost::format fmter("%.0f");
    fmter % adc;
    return fmter.str();
  }

  std::string RepRapTableWriter::formatTemperature(Temperature t) const
  {
    boost::format fmter("%.0f");
    fmter % t;
    return fmter.str();
  }

}

